<?php


namespace EasySwoole\EasySwoole;


use App\Component\Jwt;
use App\EventHandler\HandlerBase;
use App\EventHandler\HandlerRegister;
use App\Process\LmtProcess;
use App\Process\LmtQueue;
use EasySwoole\Component\Di;
use EasySwoole\Consul\Consul;
use EasySwoole\Consul\Request\Kv;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\EasySwoole\Config as ESConfig;
use EasySwoole\EasySwoole\Crontab\Crontab;
use EasySwoole\EasySwoole\Swoole\EventHelper;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\Mysqli\QueryBuilder;
use EasySwoole\ORM\Db\Config;
use EasySwoole\ORM\Db\Connection;
use EasySwoole\ORM\Db\Result;
use EasySwoole\ORM\DbManager;
use Swoole\Server;
use EasySwoole\FileWatcher\FileWatcher;
use EasySwoole\FileWatcher\WatchRule;


class EasySwooleEvent implements Event{
    public static function initialize(){
        date_default_timezone_set('Asia/Shanghai');
        //重定义日志处理器
        \EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::LOGGER_HANDLER, new \App\Core\Logger());
        \EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::HTTP_GLOBAL_ON_REQUEST, function (\EasySwoole\Http\Request $request, \EasySwoole\Http\Response $response): bool {
            $point = \EasySwoole\Tracker\PointContext::getInstance()->createStart('onRequest');
            $point->setStartArg([
                'uri' => $request->getUri()->__toString(),
                'get' => $request->getQueryParams()
            ]);

            ###### 处理请求的跨域问题 ######
            $origin = $request->getHeaderLine('origin') ?: '*';
            $response->withHeader('Access-Control-Allow-Origin', $origin);
            $response->withHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
            $response->withHeader('Access-Control-Allow-Credentials', 'true');
            $response->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, token, jwt-token');
            $response->withHeader('Access-Control-Expose-Headers', 'jwt-token');
            if ($request->getMethod() === 'OPTIONS') {
                $response->withStatus(\EasySwoole\Http\Message\Status::CODE_OK);
                return false;
            }

            return true;
        });

        foreach(HandlerRegister::getHandler() as $class){
            $obj = new $class();
            if($obj instanceof HandlerBase){
                $obj->handle();
            }
        }
    }

    public static function mainServerCreate(EventRegister $register){
        //初始化数据库
        self::initMysqlConnect();
        //热重启
        self::fileReload();
        //定时任务
        self::crontabEvent();
        //其它注册
        self::otherEventRegister();
        //注册进程
        self::initProcess();
    }

    private static function initMysqlConnect(){
        //MYSQL只读(默认，写入数据需指定链接)
        $mysqlConfigRead = ESConfig::getInstance()->getConf('MYSQL_READ');
        $configRead = new Config();
        $configRead->setDatabase($mysqlConfigRead['database']);
        $configRead->setUser($mysqlConfigRead['user']);
        $configRead->setPassword($mysqlConfigRead['password']);
        $configRead->setHost($mysqlConfigRead['host']);
        $configRead->setPort($mysqlConfigRead['port']);
        $configRead->setTimeout($mysqlConfigRead['timeout']); // 超时时间

        //连接池配置
        $configRead->setGetObjectTimeout($mysqlConfigRead['ObjectTimeout']); //设置获取连接池对象超时时间
        $configRead->setIntervalCheckTime($mysqlConfigRead['IntervalCheckTime']); //设置检测连接存活执行回收和创建的周期
        $configRead->setMaxIdleTime($mysqlConfigRead['MaxIdleTime']); //连接池对象最大闲置时间(秒)
        $configRead->setMinObjectNum($mysqlConfigRead['MinObjectNum']); //设置最小连接池存在连接对象数量
        $configRead->setMaxObjectNum($mysqlConfigRead['MaxObjectNum']); //设置最大连接池存在连接对象数量
        $configRead->setAutoPing($mysqlConfigRead['AutoPing']); //设置自动ping客户端链接的间隔
        $configRead->setReturnCollection($mysqlConfigRead['returnCollection']);
        DbManager::getInstance()->addConnection(new Connection($configRead));

        //MYSQL读写
        $mysqlConfigWrite = ESConfig::getInstance()->getConf('MYSQL_WRITE');
        $configWrite = new Config();
        $configWrite->setDatabase($mysqlConfigWrite['database']);
        $configWrite->setUser($mysqlConfigWrite['user']);
        $configWrite->setPassword($mysqlConfigWrite['password']);
        $configWrite->setHost($mysqlConfigWrite['host']);
        $configWrite->setPort($mysqlConfigWrite['port']);
        $configWrite->setTimeout($mysqlConfigWrite['timeout']); // 超时时间

        //连接池配置
        $configWrite->setGetObjectTimeout($mysqlConfigWrite['ObjectTimeout']); //设置获取连接池对象超时时间
        $configWrite->setIntervalCheckTime($mysqlConfigWrite['IntervalCheckTime']); //设置检测连接存活执行回收和创建的周期
        $configWrite->setMaxIdleTime($mysqlConfigWrite['MaxIdleTime']); //连接池对象最大闲置时间(秒)
        $configWrite->setMinObjectNum($mysqlConfigWrite['MinObjectNum']); //设置最小连接池存在连接对象数量
        $configWrite->setMaxObjectNum($mysqlConfigWrite['MaxObjectNum']); //设置最大连接池存在连接对象数量
        $configWrite->setAutoPing($mysqlConfigWrite['AutoPing']); //设置自动ping客户端链接的间隔
        $configWrite->setReturnCollection($mysqlConfigWrite['returnCollection']);
        DbManager::getInstance()->addConnection(new Connection($configWrite),'write');

        //SQL执行回调处理，记录日志
        DbManager::getInstance()->onQuery(function($res, $builder, $start){
            //记录日志
            $runTime = bcsub(microtime(true), $start, 5);
            /** @var QueryBuilder $builder */
            $sqlString = $builder->getLastQuery();
            $pid = getmypid();
            /** @var Result $res */
            $affectedRows = $res->getAffectedRows();
            //格式：进程ID | SQL执行时间 | SQL语句 | SQL影响行数（针对insert以及update）
            $recordString = $pid . ' | ' . $runTime . ' | ' . $sqlString . ' | ' . $affectedRows;
            \EasySwoole\EasySwoole\Logger::getInstance()->info($recordString, 'sql');
        });

    }
    private static function fileReload(){
        //MYSQL只读(默认，写入数据需指定链接)
        $mysqlConfigRead = ESConfig::getInstance()->getConf('MYSQL_READ');
        $configRead = new Config();
        $configRead->setDatabase($mysqlConfigRead['database']);
        $configRead->setUser($mysqlConfigRead['user']);
        $configRead->setPassword($mysqlConfigRead['password']);
        $configRead->setHost($mysqlConfigRead['host']);
        $configRead->setPort($mysqlConfigRead['port']);
        $configRead->setTimeout($mysqlConfigRead['timeout']); // 超时时间

        //连接池配置
        $configRead->setGetObjectTimeout($mysqlConfigRead['ObjectTimeout']); //设置获取连接池对象超时时间
        $configRead->setIntervalCheckTime($mysqlConfigRead['IntervalCheckTime']); //设置检测连接存活执行回收和创建的周期
        $configRead->setMaxIdleTime($mysqlConfigRead['MaxIdleTime']); //连接池对象最大闲置时间(秒)
        $configRead->setMinObjectNum($mysqlConfigRead['MinObjectNum']); //设置最小连接池存在连接对象数量
        $configRead->setMaxObjectNum($mysqlConfigRead['MaxObjectNum']); //设置最大连接池存在连接对象数量
        $configRead->setAutoPing($mysqlConfigRead['AutoPing']); //设置自动ping客户端链接的间隔
        $configRead->setReturnCollection($mysqlConfigRead['returnCollection']);
        DbManager::getInstance()->addConnection(new Connection($configRead));

        //MYSQL读写
        $mysqlConfigWrite = ESConfig::getInstance()->getConf('MYSQL_WRITE');
        $configWrite = new Config();
        $configWrite->setDatabase($mysqlConfigWrite['database']);
        $configWrite->setUser($mysqlConfigWrite['user']);
        $configWrite->setPassword($mysqlConfigWrite['password']);
        $configWrite->setHost($mysqlConfigWrite['host']);
        $configWrite->setPort($mysqlConfigWrite['port']);
        $configWrite->setTimeout($mysqlConfigWrite['timeout']); // 超时时间

        //连接池配置
        $configWrite->setGetObjectTimeout($mysqlConfigWrite['ObjectTimeout']); //设置获取连接池对象超时时间
        $configWrite->setIntervalCheckTime($mysqlConfigWrite['IntervalCheckTime']); //设置检测连接存活执行回收和创建的周期
        $configWrite->setMaxIdleTime($mysqlConfigWrite['MaxIdleTime']); //连接池对象最大闲置时间(秒)
        $configWrite->setMinObjectNum($mysqlConfigWrite['MinObjectNum']); //设置最小连接池存在连接对象数量
        $configWrite->setMaxObjectNum($mysqlConfigWrite['MaxObjectNum']); //设置最大连接池存在连接对象数量
        $configWrite->setAutoPing($mysqlConfigWrite['AutoPing']); //设置自动ping客户端链接的间隔
        $configWrite->setReturnCollection($mysqlConfigWrite['returnCollection']);
        DbManager::getInstance()->addConnection(new Connection($configWrite),'write');

        //SQL执行回调处理，记录日志
        DbManager::getInstance()->onQuery(function($res, $builder, $start){
            //记录日志
            $runTime = bcsub(microtime(true), $start, 5);
            /** @var QueryBuilder $builder */
            $sqlString = $builder->getLastQuery();
            $pid = getmypid();
            /** @var Result $res */
            $affectedRows = $res->getAffectedRows();
            //格式：进程ID | SQL执行时间 | SQL语句 | SQL影响行数（针对insert以及update）
            $recordString = $pid . ' | ' . $runTime . ' | ' . $sqlString . ' | ' . $affectedRows;
            if('produce' <> ESConfig::getInstance()->getConf('ENV')) {
                \EasySwoole\EasySwoole\Logger::getInstance()->info($recordString, 'sql');
            }
        });

    }

    private static function crontabEvent(){
        // 配置定时任务
        $crontabConfig = new \EasySwoole\Crontab\Config();

        // 1.设置执行定时任务的 socket 服务的 socket 文件存放的位置，默认值为 当前文件所在目录
        // 这里设置为框架的 Temp 目录
        $crontabConfig->setTempDir(\EasySwoole\EasySwoole\Config::getInstance()->getConf('TEMP_DIR'));

        // 2.设置执行定时任务的 socket 服务的名称，默认值为 'EasySwoole'
        $crontabConfig->setServerName('EasySwoole');

        // 3.设置用来执行定时任务的 worker 进程数，默认值为 3
        $crontabConfig->setWorkerNum(3);

        // 4.设置定时任务执行出现异常的异常捕获回调
        $crontabConfig->setOnException(function (\Throwable $throwable) {
            // 定时任务执行发生异常时触发（如果未在定时任务类的 onException 中进行捕获异常则会触发此异常回调）
        });

        // 创建定时任务实例
        $crontab = \EasySwoole\EasySwoole\Crontab\Crontab::getInstance($crontabConfig);

        // 注册定时任务
//        $crontab->register(new \App\Crontab\TaskDay());
        $crontab->register(new \App\Crontab\TaskMinute());
    }
    public static function initProcess()
    {
        // 配置 队列驱动器
        LmtQueue::getInstance(new \EasySwoole\Queue\Driver\RedisQueue(new \EasySwoole\Redis\Config\RedisConfig(ESConfig::getInstance()->getConf('REDIS')), 'lmt_queue'));
        // 注册一个消费进程
        $processConfig = new \EasySwoole\Component\Process\Config([
            'processName'     => 'lmt_queue', // 设置 自定义进程名称
            'processGroup'    => 'LmtQueue', // 设置 自定义进程组名称
            'enableCoroutine' => true, // 设置 自定义进程自动开启协程
        ]);
        \EasySwoole\Component\Process\Manager::getInstance()->addProcess(new LmtProcess($processConfig));
    }

    private static function otherEventRegister(){
        //事件注册
        $register = ServerManager::getInstance()->getEventRegister();

        //注册redis处理组件
        $redisConf = ESConfig::getInstance()->getConf('REDIS');
        if($redisConf){
            $redis = new \EasySwoole\Redis\Redis(new \EasySwoole\Redis\Config\RedisConfig($redisConf));
            Di::getInstance()->set('REDIS', $redis);
        }

        //注册Redis连接池
        \EasySwoole\RedisPool\RedisPool::getInstance()->register(new \EasySwoole\Redis\Config\RedisConfig($redisConf));

        /*Mongo注册*/
        $mongoDbConf = ESConfig::getInstance()->getConf('MONGODB');
        if($mongoDbConf){
            Di::getInstance()->set('MONGODB', \App\Lib\Mongodb\MongodbDriver::class, $mongoDbConf);
        }

        EventHelper::registerWithAdd($register, EventRegister::onWorkerStart, function(Server $server, $workerId){
            foreach(HandlerRegister::getHandler() as $class){
                $obj = new $class();
                if($obj instanceof HandlerBase){
                    $obj->handle();
                }
            }
            //注册JWT处理组件
            $jwt = new Jwt('KY-JWT');
            Di::getInstance()->set('jwt', $jwt);

            DbManager::getInstance()->getConnection()->__getClientPool()->keepMin();
            DbManager::getInstance()->getConnection('write')->__getClientPool()->keepMin();
        });
    }
}
